/**
 * Copyright (c) 2012, Camelot and/or its affiliates. All rights reserved.
 * Camelot PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.camelot.ccr.global.dao.genericimpl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.Transformers;
import org.hibernate.type.StandardBasicTypes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;

import com.camelot.ccr.global.dao.GenericDaO;
import com.camelot.ccr.global.util.Pager;
import com.camelot.ccr.global.util.Utility;
/**
 * <pre>
 * 泛型基类实现类
 * </pre>
 * 
 * @param <T>
 *            泛型类
 * @param <ID>
 *            泛型主键
 * @author Liu Jun
 * @version V0.1, 2013-12-26 下午2:19:49
 */
public abstract class GenericHibernateDaoImpl<T, ID extends Serializable> implements GenericDaO<T, ID> {

	private static Logger logger = Logger.getLogger(GenericHibernateDaoImpl.class);
	private static int SQL_TYPE = 0;
	private static int HQL_TYPE = 1;
	private static String COUNT_PREFIX = "select count(*) ";
	private static String SQL_COUNT_PREFIX = "select count(*) COUNT ";

	private Class<T> clazz;
	@Autowired
	private SessionFactory sessionFactory;

	/**
	 * 泛型构造方法
	 */
	@SuppressWarnings("unchecked")
	public GenericHibernateDaoImpl() {
		logger.debug("Begin  initialize GenericHibernateDAOImpl");
		clazz = null;
		Class<T> c = (Class<T>) getClass();
		if (c != null) {
			Type t = c.getGenericSuperclass();
			if (t instanceof ParameterizedType) {
				Type[] p = ((ParameterizedType) t).getActualTypeArguments();
				clazz = (Class<T>) p[0];
				logger.debug("get ParameterizedType  in GenericHibernateDAOImpl");
			}
		}
	}

	@Override
	public void save(T t) throws DataAccessException {
		getCurrentSession().save(t);
	}

	@Override
	public void delete(T t) throws DataAccessException {
		getCurrentSession().delete(t);
	}

	@SuppressWarnings({ "hiding", "unchecked" })
	@Override
	public <T> T get(final ID id) throws DataAccessException {
		return (T) getCurrentSession().get(clazz, id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> getAll() throws DataAccessException {
		return createTQuery(null).list();
	}

	@Override
	public Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	@Override
	public Query createQuery(final String hql) throws DataAccessException {
		return getCurrentSession().createQuery(hql);
	}

	@Override
	public Query createTQuery(String hql) throws DataAccessException {
		String entityHql = "from " + clazz.getName();
		if (Utility.isNotNull(hql)) {
			entityHql += hql;
		}
		return createQuery(entityHql);
	}

	@Override
	public void update(T t) throws DataAccessException {
		getCurrentSession().update(t);
	}
	
	@Override
	public void merge(T t) throws DataAccessException {
		getCurrentSession().merge(t);
	}

	@Override
	public void saveOrUpdate(T t) throws DataAccessException {
		getCurrentSession().saveOrUpdate(t);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findById(final ID id) throws DataAccessException {
		return (T) getCurrentSession().get(clazz, id);
	}

	@Override
	public <T> List<T> findByProperty(final String proName, final String proValue) throws DataAccessException {
		Query query = createQuery("from " + clazz.getName() + " where " + proName + "= ?").setParameter(0, proValue);
		return query.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findListByProperty(String propertyName, Object value) {
		
		Criteria criteria = this.createCriteria();
		criteria.add(Restrictions.eq(propertyName, value));
		return criteria.list();
	}

	@Override
	public Criteria createCriteria() throws DataAccessException {
		return getCurrentSession().createCriteria(clazz);
	}

	@Override
	public <T> List<T> findByExample(final T exampleEntity, final int firstResult, final int maxResults)
		throws DataAccessException {
		return findByExampleOrder(exampleEntity, firstResult, maxResults, null);
	}

	@Override
	public <T> List<T> findByExampleOrder(T exampleEntity, int firstResult, int maxResults, Order... orders)
		throws DataAccessException {
		Criteria executableCriteria = createCriteria();
		executableCriteria.add(Example.create(exampleEntity));
		for (int i = 0; orders != null && i < orders.length; i++) {
			executableCriteria.addOrder(orders[i]); // 对结果进行排序
		}
		if (firstResult >= 0) {
			executableCriteria.setFirstResult(firstResult);
		}
		if (maxResults > 0) {
			executableCriteria.setMaxResults(maxResults);
		}
		return executableCriteria.list();
	}

	@Override
	public <T> List<T> findByCondition(Map<String, Object> map) throws DataAccessException {
		StringBuffer sb = new StringBuffer("from " + clazz.getName() + " where 1=1 ");

		Set keySet = map.keySet();
		Iterator iterator = keySet.iterator();
		while (iterator.hasNext()) {
			String key = String.valueOf(iterator.next());
			Object keyName = map.get(key);

			sb.append(" and " + key + " = '" + keyName + "'");
			logger.debug(String.format("%s=[%s]", new Object[] { "key", keyName }));
		}

		return createQuery(sb.toString()).list();
	}

	@Override
	public SQLQuery createSQLQuery(final String sql) throws DataAccessException {
		return getCurrentSession().createSQLQuery(sql);
	}

	@Override
	public List<?> find(String hqlOrSql, Collection<?> params, int type) throws DataAccessException {
		Query q = null;
		if (type == HQL_TYPE) {
			q = createQuery(hqlOrSql);
		} else {
			q = createSQLQuery(hqlOrSql);
		}
		setParametersValue(q, params);
		logger.debug(String.format("%s=[%s]", new Object[] { "find", hqlOrSql }));
		return q.list();
	}

	@Override
	public List<?> findByHQL(final String hql, final Collection<?> params) {
		return find(hql, params, HQL_TYPE);
	}

	@Override
	public List<?> findBySQL(String sql, Collection<?> params) throws DataAccessException {
		return find(sql, params, SQL_TYPE);
	}

	/**
	 * <pre>
	 * 判断是否为集合
	 * </pre>
	 * 
	 * @param obj
	 *            要判断的对象
	 * @return 如果是则返回true否则false
	 * @throws DataAccessException
	 * @author Liu Jun, 2013-12-26 下午7:00:08
	 * @throws DataAccessException
	 *             数据库异常
	 */
	private boolean isCollection(Object obj) throws DataAccessException {
		if (obj == null) {
			return false;
		}
		
		return obj instanceof Collection;
	}

	/**
	 * 更新hql的赋值
	 * 
	 * @param q
	 *            Query对象
	 * @param values
	 *            需要修改的map记录
	 * @throws DataAccessException
	 *             数据库异常
	 */
	private void setParametersValue(Query q, Map<String, Object> values) throws DataAccessException {
		if (q == null) {
			return;
		}
		if (values != null) {
			Iterator i$ = values.keySet().iterator();
			do {
				if (!i$.hasNext()) {
					break;
				}
				String key = (String) i$.next();
				Object value = values.get(key);
				if (value != null) {
					if (isCollection(value)) {
						q.setParameterList(key, (Collection) value);
						if (logger.isDebugEnabled()) {
							StringBuilder sb = new StringBuilder("[");
							Object[] arr$ = (Object[]) value;
							int len$ = arr$.length;
							for (int j = 0; j < len$; j++) {
								Object obj = arr$[j];
								if (obj == null) {
									sb.append("null").append(",");
								} else {
									sb.append(obj.toString()).append(",");
								}
							}
							sb.append("]");
							logger.debug(String.format("%s=[%s]", new Object[] { key, sb.toString() }));
						}
					} else {
						q.setParameter(key, value);
						if (logger.isDebugEnabled()) {
							String s = value != null ? value.toString() : "null";
							logger.debug(String.format("%s=[%s]", new Object[] { key, s }));
						}
					}
				}
			} while (true);
		}
	}

	/**
	 * <pre>
	 * 为q设置参数值
	 * </pre>
	 * 
	 * @param q
	 *            query对象
	 * @param values
	 *            集合类行值
	 * @throws DataAccessException
	 *             数据库异常
	 * @author Liu Jun, 2013-12-27 下午2:04:00
	 */
	private void setParametersValue(Query q, Collection<?> values) throws DataAccessException {
		if (Utility.isNotEmpty(values)) {
			Iterator iterator = values.iterator();
			int i = 0;
			while (iterator.hasNext()) {
				q.setParameter(i++, iterator.next());
			}
		}
	}
	
	@Override
	public int updateByHql(final String updateHql, final Map<String, Object> values) throws DataAccessException {
		Query q = createQuery(updateHql);
		setParametersValue(q, values);
		return q.executeUpdate();
	}

	@Override
	public int updateBySql(final String updateSql, final Map<String, Object> values) throws DataAccessException {
		Query q = createSQLQuery(updateSql);
		setParametersValue(q, values);
		return q.executeUpdate();
	}

	@Override
	public void deleteAll(Collection<T> entities) throws DataAccessException {
		Session session = getCurrentSession();
		for (Object entity : entities) {
			session.delete(entity);
		}
	}

	@Override
	public <T> List<T> findByExample(T exampleEntity) throws DataAccessException {
		return findByExample(exampleEntity, -1, -1);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> queryListPage(String hql, int firstResult, int maxResult) throws DataAccessException {
		Query q = createQuery(hql);
		q.setFirstResult(firstResult);
		q.setMaxResults(maxResult);
		return q.list();
	}

	@Override
	public Long countEntity() throws DataAccessException {
		StringBuffer sb = new StringBuffer();
		sb.append(COUNT_PREFIX);
		sb.append("from ");
		sb.append(clazz.getName());
		Query query = createQuery(sb.toString());
		return (Long) query.uniqueResult();
	}

	@Override
	public Long countEntity(final String hql, final Map<String, Object> values) throws DataAccessException {
		String conditionHql = null;
		if (Utility.isNotEmpty(hql)) {
			conditionHql = hql.substring(hql.indexOf("from"));
			conditionHql = COUNT_PREFIX + conditionHql;
		}

		Query q = (conditionHql == null ? createTQuery(null) : createQuery(conditionHql));
		setParametersValue(q, values);
		return (Long) q.uniqueResult();
	}

	@Override
	public Long countEntity(String hqlOrSql, Collection<?> values, int type) throws DataAccessException {
		Query q = null;
		String condition = hqlOrSql.substring(hqlOrSql.lastIndexOf("from"));
		if (hqlOrSql.lastIndexOf("order") > 0) {// 如果遇到order by排序 则截取掉
			condition = condition.substring(0, condition.lastIndexOf("order") - 1);
		}
		condition = COUNT_PREFIX + condition;
		q = type == SQL_TYPE ? createSQLQuery(condition) : createQuery(condition);

		setParametersValue(q, values);
		
		/**chendd add start 20140109*/
		Object count = q.uniqueResult();
		if(count instanceof BigInteger){
			BigInteger bigInteger = (BigInteger) count;
			return bigInteger.longValue();
		}
		/**chendd add end*/
		
		return (Long) q.uniqueResult();
	}
	
	@Override
	public Long countMapEntity(String hqlOrSql, Collection<?> values) throws DataAccessException {
		Query q = null;
		if (hqlOrSql.lastIndexOf("order") > 0) {// 如果遇到order by排序 则截取掉
			hqlOrSql = hqlOrSql.substring(0, hqlOrSql.lastIndexOf("order") - 1);
		}
		StringBuilder condition = new StringBuilder(COUNT_PREFIX);
		condition.append(" from (").append(hqlOrSql).append(") countEntity ");
		q = createSQLQuery(condition.toString());
		setParametersValue(q, values);
		
		/**chendd add start 20140109*/
		Object count = q.uniqueResult();
		if(count instanceof BigInteger){
			BigInteger bigInteger = (BigInteger) count;
			return bigInteger.longValue();
		}
		/**chendd add end*/
		
		return (Long) q.uniqueResult();
	}

	@Override
	public Long countEntityByHql(final String hql, final Collection<?> values) throws DataAccessException {
		return countEntity(hql, values, HQL_TYPE);
	}

	@Override
	public Long countEntityBySql(String sql, Collection<?> values) throws DataAccessException {
		return countEntity(sql, values, SQL_TYPE);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Pager<?> queryListPageByHql(final String hql, Map<String, Object> values, int curpage, int pagesize)
		throws DataAccessException {
		Pager<?> pager = new Pager(curpage, pagesize);
		Query q = hql == null ? createTQuery(hql) : createQuery(hql);
		setParametersValue(q, values);
		q.setFirstResult(pager.getStart());
		q.setMaxResults(pagesize);

		long sum = countEntity(hql, values);
		pager.setSum(sum);
		pager.setSearchList(q.list());
		return pager;
	}

	@Override
	public Pager<?> queryListPageByHql(final String hql, final int curpage, final int pagesize, final Collection<?> values)
		throws DataAccessException {
		return queryListPage(hql, curpage, pagesize, values, HQL_TYPE);
	}

	@Override
	public Pager<?> queryListPageByHql(final String hql, final Pager<?> pager, final Collection<?> values)
		throws DataAccessException {
		return queryListPageByHql(hql, pager.getPage(), pager.getRows(), values);
	}

	@Override
	public Pager<?> queryListPage(final String hqlOrSql, final int curpage, final int pagesize, final Collection<?> values,
			final int type) throws DataAccessException {
		Query q = null;
		long sum = 0;
		Pager<?> pager = new Pager(curpage, pagesize);
		if (type == SQL_TYPE) {
			q = createSQLQuery(hqlOrSql);
			sum = countEntity(hqlOrSql, values, SQL_TYPE);
		} else {
			q = createQuery(hqlOrSql);
			sum = countEntity(hqlOrSql, values, HQL_TYPE);
		}
		setParametersValue(q, values);

		q.setFirstResult(pager.getStart());
		q.setMaxResults(pagesize);

		pager.setSum(sum);
		pager.setSearchList(q.list());
		return pager;
	}

	@Override
	public Pager<?> queryListPageBySql(final String hql, final int curpage, final int pagesize, Collection<?> values)
		throws DataAccessException {
		return queryListPage(hql, curpage, pagesize, values, SQL_TYPE);
	}

	@Override
	public Pager<?> queryListPageBySql(final String hql, final Pager<?> pager, final Collection<?> values)
		throws DataAccessException {
		return queryListPageBySql(hql, pager.getPage(), pager.getRows(), values);
	}
	/**
	 * <pre>
	 * 根据参数map键值传参 例：   :name = name 查询分页
	 * </pre>
	 * @param sql sql
	 * @param curpage 当前页
	 * @param pagesize 每页显示条数
	 * @param values 参数结果集
	 * @param scalars  类型
	 * @return Pager<List<Map<String, Object>>>
	 * @throws DataAccessException 数据库异常
	 * @author Liu Jun, 2014-1-14 上午10:42:01
	 */
	public Pager<Map<String, Object>> queryListMapPageBySql(final String sql, final int curpage,
		final int pagesize, final Collection<?> values, Map<String, org.hibernate.type.Type> scalars)
		throws DataAccessException {
		SQLQuery q = null;
		long sum = 0;
		Pager<Map<String, Object>> pager = new Pager<Map<String, Object>>(curpage, pagesize);
		q = createSQLQuery(sql);
		sum = countSQLEntity(sql, values);
		setParametersValue(q, values);
		addScalars(q, scalars);
		q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		q.setFirstResult(pager.getStart());
		q.setMaxResults(pagesize);
		pager.setSum(sum);
		pager.setSearchList(q.list());
		return pager;
	}

	/**
	 * <pre>
	 * 设置字段
	 * </pre>
	 * @param query hibernatenate Query对象
	 * @param scalars 字段名  及类型的集合
	 * @author Fu Dawei, 2014-1-8 下午6:00:40
	 */
	private void addScalars(SQLQuery query, Map<String, org.hibernate.type.Type> scalars) {
		for (Map.Entry<String, org.hibernate.type.Type> entry : scalars.entrySet()) {
			query.addScalar(entry.getKey(), entry.getValue());
		}
	}
	
	/**
	 * 分页查询 返回Map
	 * <pre>
	 * <请输入描述信息>
	 * </pre>
	 * @param sql sql语句
	 * @param pager 分页对象
	 * @param values  参数集合
	 * @param scalars  查询字段及类型map
	 * @return pager 分页对象
	 * @author Fu Dawei, 2014-1-8 下午6:23:35
	 */
	public Pager<Map<String, Object>> queryListMapPageBySql(
			final String sql, Pager<Map<String, Object>> pager, 
			final Collection<?> values,
			Map<String, org.hibernate.type.Type> scalars){
		return queryListMapPageBySql(sql,pager.getPage(), pager.getRows(),values, scalars);
	}
	

	@Override
	public Pager<?> queryListMapPageBySql(String sql, int curpage,
			int pagesize, Collection<?> values) {
		
//		long sum = countEntity(sql, values, SQL_TYPE);//TODO dengzy 修改，不确定是否会影响暂时注释
		long sum = countMapEntity(sql, values);
		Pager<?> pager = new Pager(curpage, pagesize);
		Query q = createSQLQuery(sql);
		setParametersValue(q, values);
		q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		q.setFirstResult(pager.getStart());
		q.setMaxResults(pagesize);
		pager.setSum(sum);
		pager.setSearchList(q.list());
		return pager;
	}

	@Override
	public List<Map<String , Object>> queryListMapBySql(String sql , Collection<?> values) {
		Query q = createSQLQuery(sql);
		setParametersValue(q, values);
		q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		return q.list();
	}
	
	@Override
	public List<Map<String , Object>> queryListMapBySql(String sql , Object... values) {
		
		return this.queryListMapBySql(sql, Arrays.asList(values));
	}
	
	/**
	 * <pre>
	 * 计算sql语句的总记录数
	 * </pre>
	 * @param sql sql
	 * @param values values
	 * @return Long
	 * @throws DataAccessException DataAccessException
	 * @author Liu Jun, 2014-1-9 下午6:32:54
	 */
	private Long countSQLEntity(String sql, Collection<?> values) 
		throws DataAccessException {
		SQLQuery q = null;
		String condition = "select count(1) COUNT FROM ("   + sql  + ") t ";
		q = createSQLQuery(condition);
		q.addScalar("COUNT", StandardBasicTypes.LONG);
		setParametersValue(q, values);
		Long lo = q.uniqueResult() != null ? (Long) q.uniqueResult() : 0L;
		return lo;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public T queryEntityBySql(String sql , Collection<?> values){
		
		Query q = createSQLQuery(sql);
		setParametersValue(q, values);
		q.setResultTransformer(Transformers.aliasToBean(clazz));
		return (T) q.uniqueResult();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> queryListBySql(String sql, Collection<?> values) {
		
		Query q = createSQLQuery(sql);
		setParametersValue(q, values);
		q.setResultTransformer(Transformers.aliasToBean(clazz));
		return q.list();
	}

	@Override
	public List<T> queryListBySql(String sql, Object... values) {
	
		return this.queryListBySql(sql, Arrays.asList(values));
	}

	@SuppressWarnings("unchecked")
	@Override
	public Pager<T> queryPageBySql(String sql, int pageNo , int pageSize,
			Collection<?> values) {
		
		Pager<T> pager = new Pager<T>(pageNo, pageSize);
		long sum = countEntity(sql, values, SQL_TYPE);
		List<T> dataList = new ArrayList<T>();
		//如果查询到记录数
		if(sum > 0){
			Query q = createSQLQuery(sql);
			setParametersValue(q, values);
			q.setResultTransformer(Transformers.aliasToBean(clazz));
			q.setFirstResult(pager.getStart());
			q.setMaxResults(pager.getRows());
			dataList = q.list();
		}
		pager.setSearchList(dataList);
		pager.setSum(sum);
		return pager;
	}
	
	@Override
	public Pager<T> queryPageBySql(String sql, int pageNo , int pageSize,
			Object... values) {
		
		return this.queryPageBySql(sql, pageNo, pageSize, Arrays.asList(values));
	}

	@Override
	public int executeBySql(String sql, Collection<?> values) {

		Query q = createSQLQuery(sql);
		setParametersValue(q, values);
		return q.executeUpdate();
	}
	
	@Override
	public int executeBySql(String sql, Object ...params) {
		
		return this.executeBySql(sql, Arrays.asList(params));
	}

	@Override
	public <T> void batchSave(List<T> entitys) {
		for (int i = 0; i < entitys.size(); i++) {
			getCurrentSession().save(entitys.get(i));
			if ((i != 0) && (i % 20 == 0)) {
				// 20个对象后才清理缓存，写入数据库
				getCurrentSession().flush();
				getCurrentSession().clear();
			}
		}
		// 最后清理一下----防止大于20小于40的不保存
		getCurrentSession().flush();
		getCurrentSession().clear();
	}

	@Override
	public <T> void batchUpdate(List<T> entitys) {
		for (int i = 0; i < entitys.size(); i++) {
			getCurrentSession().update(entitys.get(i));
			if ((i != 0) && (i % 20 == 0)) {
				// 20个对象后才清理缓存，写入数据库
				getCurrentSession().flush();
				getCurrentSession().clear();
			}
		}
		// 最后清理一下----防止大于20小于40的不保存
		getCurrentSession().flush();
		getCurrentSession().clear();
	}

	@Override
	public <T> void batchDelete(List<T> entitys) {
		for (int i = 0; i < entitys.size(); i++) {
			getCurrentSession().delete(entitys.get(i));
			if ((i != 0) && (i % 20 == 0)) {
				// 20个对象后才清理缓存，写入数据库
				getCurrentSession().flush();
				getCurrentSession().clear();
			}
		}
		// 最后清理一下----防止大于20小于40的不保存
		getCurrentSession().flush();
		getCurrentSession().clear();
		
	}
	
}
